package com.czh.subject.domain.service.impl;

import com.czh.subject.common.entity.PageResult;
import com.czh.subject.common.enums.SubjectIsDeleteEnum;
import com.czh.subject.common.utils.IdWorkerUtil;
import com.czh.subject.common.utils.UserUtil;
import com.czh.subject.domain.convert.SubjectESConverter;
import com.czh.subject.domain.convert.SubjectInfoConverter;
import com.czh.subject.domain.entity.SubjectAnswerAndOptionBO;
import com.czh.subject.domain.entity.SubjectInfoBO;
import com.czh.subject.domain.handler.subject.SubjectTypeHandler;
import com.czh.subject.domain.handler.subject.SubjectTypeHandlerFactory;
import com.czh.subject.domain.redis.RedisUtil;
import com.czh.subject.domain.service.SubjectInfoDomainService;
import com.czh.subject.domain.service.SubjectLikedDomainService;
import com.czh.subject.infra.basic.entity.SubjectInfo;
import com.czh.subject.infra.basic.entity.SubjectLabel;
import com.czh.subject.infra.basic.entity.SubjectMapping;
import com.czh.subject.infra.basic.service.SubjectInfoService;
import com.czh.subject.infra.basic.service.SubjectLabelService;
import com.czh.subject.infra.basic.service.SubjectMappingService;
import com.czh.subject.infra.basic.entity.SubjectInfoES;
import com.czh.subject.infra.basic.service.SubjectEsService;
import com.czh.subject.infra.rpc.UserRPC;
import com.czh.subject.infra.rpc.entity.UserInfo;
import jakarta.annotation.Resource;
import lombok.extern.log4j.Log4j2;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDate;
import java.util.*;

/**
 * @author czh01
 * @version 1.0
 * @since 2024/7/31 上午 12:27
 */
@Service("subjectInfoDomainService")
@Transactional(rollbackFor = Exception.class)
@Log4j2
public class SubjectInfoDomainServiceImpl implements SubjectInfoDomainService {
    @Resource
    private SubjectInfoService subjectInfoService;
    @Resource
    private SubjectTypeHandlerFactory subjectTypeHandlerFactory;
    @Resource
    private SubjectMappingService subjectMappingService;
    @Resource
    private SubjectLabelService subjectLabelService;
    @Resource
    private SubjectEsService subjectEsService;
    @Resource
    private SubjectLikedDomainService subjectLikedDomainService;
    @Resource
    private RedisUtil redisUtil;
    @Resource
    private UserRPC userRPC;

    private static final String RANKING_KEY = "subjectRanking";

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean addSubject(SubjectInfoBO subjectInfoBO) {
        SubjectInfo subjectInfo = SubjectInfoConverter.INSTANCE.convert(subjectInfoBO);
        try {
            subjectInfoService.insert(subjectInfo);
            //根据类型调用不同的题目处理器
            SubjectTypeHandler subjectTypeHandler = subjectTypeHandlerFactory.getSubjectTypeHandler(subjectInfo.getSubjectType());
            subjectInfoBO.setId(subjectInfo.getId());
            subjectTypeHandler.add(subjectInfoBO);
            //添加题目与标签和分类的映射 TODO tip
            List<SubjectMapping> subjectMappingList = new LinkedList<>();
            subjectInfoBO.getCategoryIds().forEach(category ->
                    subjectInfoBO.getLabelIds().forEach(labelId -> {
                                SubjectMapping subjectMapping = new SubjectMapping();
                                subjectMapping.setSubjectId(subjectInfo.getId());
                                subjectMapping.setCategoryId(category);
                                subjectMapping.setLabelId(labelId);
                                subjectMappingList.add(subjectMapping);
                            }
                    )
            );
            /*批量插入elasticsearch映射*/
            if (subjectMappingService.saveBatch(subjectMappingList)) {
                SubjectInfoES subjectInfoES = SubjectESConverter.INSTANCE.convert(subjectInfoBO);
                subjectInfoES.setDocId(new IdWorkerUtil(1, 1, 1).nextId());
                subjectInfoES.setCreateTime(LocalDate.now());
                subjectInfoES.setCreateUser(UserUtil.getUserId());
                //在Redis中计入排行榜
                redisUtil.setCheckSetValueIncr(RANKING_KEY, UserUtil.getUserId(), 1);
                return subjectEsService.insert(subjectInfoES);
            }
            return false;
        } catch (Exception e) {
            return false;
        }
    }

    @Override
    public PageResult<SubjectInfoBO> getSubjectPage(SubjectInfoBO subjectInfoBO) {
        PageResult<SubjectInfoBO> pageResult = new PageResult<>();
        pageResult.setPageNo(subjectInfoBO.getPageNo());
        pageResult.setPageSize(subjectInfoBO.getPageSize());
        int start = (subjectInfoBO.getPageNo() - 1) * subjectInfoBO.getPageSize();
        SubjectInfo subjectInfo = SubjectInfoConverter.INSTANCE.convert(subjectInfoBO);
        int count = subjectInfoService.countByCondition(subjectInfo, subjectInfoBO.getLabelId(), subjectInfoBO.getCategoryId());
        if (count == 0) {
            return pageResult;
        }
        List<SubjectInfo> subjectInfoList = subjectInfoService.querySubjectInfoList(subjectInfo, subjectInfoBO.getCategoryId(),
                subjectInfoBO.getLabelId(), start, subjectInfoBO.getPageSize());
        List<SubjectInfoBO> subjectInfoBOList = SubjectInfoConverter.INSTANCE.convert(subjectInfoList);
        pageResult.setResult(subjectInfoBOList);
        pageResult.setTotal(count);
        return pageResult;
    }
    @Override
    public SubjectInfoBO querySubjectInfo(SubjectInfoBO subjectInfoBO) {
        Long id = subjectInfoBO.getId();
        SubjectInfo subjectInfo = subjectInfoService.selectSubjectInfoById(id);
        subjectInfo.setIsDeleted(SubjectIsDeleteEnum.NO_DELETED.getCode());
        //根据不同题目类型获取不同的答案和选项组合类
        SubjectTypeHandler subjectTypeHandler = subjectTypeHandlerFactory.getSubjectTypeHandler(subjectInfo.getSubjectType());
        SubjectAnswerAndOptionBO subjectAnswerAndOptionBO = subjectTypeHandler.queryOptionAndAnswer(subjectInfo.getId());
        List<Long> labelIds = subjectMappingService.selectLabelBySubjectId(id);
        List<String> labelNames = subjectLabelService.selectByIds(labelIds).stream().map(SubjectLabel::getLabelName).toList();
        //本来不用SubjectAnswerAndOptionBO里面不用放嵌套对象,但是这里可以直接合并映射,也省了取值了
        SubjectInfoBO resultBO = SubjectInfoConverter.INSTANCE.convert(subjectInfo, subjectAnswerAndOptionBO);
        resultBO.setLabelName(labelNames);

        //获取点赞情况
        String userId = UserUtil.getUserId();
        Boolean liked = subjectLikedDomainService.isLiked(id.toString(), userId);
        Integer likedCount = subjectLikedDomainService.getLikedCount(subjectInfo.getId().toString());
        resultBO.setLiked(liked);
        resultBO.setLikedCount(likedCount);

        //查询前后题目的id
        extracted(subjectInfoBO, resultBO);

        return resultBO;
    }

    //查询前后题目的id
    private void extracted(SubjectInfoBO subjectInfoBO, SubjectInfoBO resultBO) {
        SubjectInfo lastSubject = subjectInfoService.getAdjoinSubject(resultBO.getId(), subjectInfoBO.getCategoryId(), subjectInfoBO.getLabelId(), 0);
        SubjectInfo nextSubject = subjectInfoService.getAdjoinSubject(resultBO.getId(), subjectInfoBO.getCategoryId(), subjectInfoBO.getLabelId(), 1);
        if (Objects.nonNull(lastSubject)){
            resultBO.setLastSubjectId(lastSubject.getId());
        }
        if (Objects.nonNull(nextSubject)){
            resultBO.setNextSubjectId(nextSubject.getId());
        }
    }

    @Override
    public PageResult<SubjectInfoBO> searchSubjectInfo(SubjectInfoBO subjectInfoBO) {
        SubjectInfoES subjectInfoES = new SubjectInfoES();
        subjectInfoES.setKeyWord(subjectInfoBO.getKeyWord());
        subjectInfoES.setSubjectType(subjectInfoBO.getSubjectType());
        PageResult<SubjectInfoES> subjectInfoESPageResult = subjectEsService.querySubjectInfoES(subjectInfoES);
        PageResult<SubjectInfoBO> result = new PageResult<>();
        result.setPageNo(subjectInfoESPageResult.getPageNo());
        result.setPageSize(subjectInfoESPageResult.getPageSize());

        List<SubjectInfoBO> subjectInfoBOS = new LinkedList<>();
        subjectInfoESPageResult.getResult().forEach(subjectES -> subjectInfoBOS.add(SubjectInfoConverter.INSTANCE.convert(subjectES)));
        result.setResult(subjectInfoBOS);
        return result;
    }

    @Override
    public List<SubjectInfoBO> getContributeList() {
        Set<ZSetOperations.TypedTuple<Object>> cacheSet = redisUtil.getCacheSet(RANKING_KEY, 0, 5);
        if (cacheSet.isEmpty()) {
            return Collections.emptyList();
        }
        List<String> userNameList = new LinkedList<>();
        Map<String, Double> map = new HashMap<>();
        cacheSet.forEach(typedTuple -> {
            userNameList.add((String) typedTuple.getValue());
            map.put((String) typedTuple.getValue(), typedTuple.getScore());
        });

        List<SubjectInfoBO> subjectInfoBOList = new LinkedList<>();
        List<UserInfo> userInfoList = userRPC.getUserInfoList(userNameList);
        if (userInfoList.isEmpty()) {
            return subjectInfoBOList;
        }
        userInfoList.forEach(userInfo -> subjectInfoBOList.add(
                new SubjectInfoBO().
                        setCreateUser(userInfo.getNickname()).
                        setCreateUserAvatar(userInfo.getAvatar()).
                        setSubjectCount(map.get(userInfo.getUsername()).intValue())
        ));
        return subjectInfoBOList;
    }
}
